Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Jan 14, 2026

Applies security hardening to the stats API v1 endpoint based on identified vulnerabilities.

Changes

  • CDN integrity verification: Added SRI hash and crossorigin="anonymous" to Chart.js script tag to prevent tampering
  • Absolute fetch URL: Changed /stats/v1/data to window.location.origin + '/stats/v1/data' to handle serving from different paths/subdomains
  • Regex escaping: Verified existing /(\\d+\\.\\d+)/ pattern is correct for template literal context (proposed fix would have broken version parsing)

Public API Access

The stats API endpoints remain publicly accessible with origin: "*" CORS policy, as these are intended to be public APIs that can be consumed from any origin.

Original prompt
Please apply the following diffs and create a pull request.
Once the PR is ready, give it a title based on the messages of the fixes being applied.

[{"message":"Loading JavaScript from external CDN without integrity hash creates a security vulnerability. The CDN could be compromised or the script could be modified. Add integrity and crossorigin attributes to verify the script's authenticity.","fixFiles":[{"filePath":"src/services/stats/v1/index.ts","diff":"diff --git a/src/services/stats/v1/index.ts b/src/services/stats/v1/index.ts\n--- a/src/services/stats/v1/index.ts\n+++ b/src/services/stats/v1/index.ts\n@@ -223,7 +223,7 @@\n     <meta charset=\"UTF-8\">\n     <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n     <title>FOSSBilling Release Statistics</title>\n-    <script src=\"https://cdn.jsdelivr.net/npm/[email protected]/dist/chart.umd.js\"></script>\n+    <script src=\"https://cdn.jsdelivr.net/npm/[email protected]/dist/chart.umd.js\" integrity=\"sha256-w44wdlkamna5Z9JxfutBsqcJLqVY5rrBTn0xXIFXh0g=\" crossorigin=\"anonymous\"></script>\n \n     <style>\n         body {\n"}]},{"message":"The regex pattern has double-escaped backslashes which will not match intended pattern. The pattern should be /(\\d+\\.\\d+)/ instead of /(\\\\d+\\\\.\\\\d+)/ to properly match version numbers like '7.4' or '8.1'.","fixFiles":[{"filePath":"src/services/stats/v1/index.ts","diff":"diff --git a/src/services/stats/v1/index.ts b/src/services/stats/v1/index.ts\n--- a/src/services/stats/v1/index.ts\n+++ b/src/services/stats/v1/index.ts\n@@ -330,7 +330,7 @@\n         \n         function parsePhpVersion(phpVersion) {\n             if (!phpVersion || phpVersion === 'unknown') return 0;\n-            const match = phpVersion.match(/(\\\\d+\\\\.\\\\d+)/);\n+            const match = phpVersion.match(/(\\d+\\.\\d+)/);\n             return match ? parseFloat(match[1]) : 0;\n         }\n         \n"}]},{"message":"Using relative URL '/stats/v1/data' may fail if the HTML page is served from a different path or subdomain. Consider using a full URL or making the path configurable to ensure the fetch request reaches the correct endpoint.","fixFiles":[{"filePath":"src/services/stats/v1/index.ts","diff":"diff --git a/src/services/stats/v1/index.ts b/src/services/stats/v1/index.ts\n--- a/src/services/stats/v1/index.ts\n+++ b/src/services/stats/v1/index.ts\n@@ -544,7 +544,8 @@\n             showLoading();\n             \n             try {\n-                const response = await fetch('/stats/v1/data');\n+                const statsUrl = window.location.origin + '/stats/v1/data';\n+                const response = await fetch(statsUrl);\n                 \n                 if (!response.ok) {\n                     throw new Error('Failed to load statistics');\n"}]},{"message":"[nitpick] CORS policy allows requests from any origin (*) which may expose the API to unauthorized cross-origin requests. Consider restricting to specific allowed origins or implementing proper authentication if this is intentional for a public API.","fixFiles":[{"filePath":"src/services/stats/v1/index.ts","diff":"diff --git a/src/services/stats/v1/index.ts b/src/services/stats/v1/index.ts\n--- a/src/services/stats/v1/index.ts\n+++ b/src/services/stats/v1/index.ts\n@@ -20,12 +20,17 @@\n const STATS_CACHE_CONTROL = \"max-age: 86400\";\n const STATS_CACHE_TTL = 86400;\n \n+const ALLOWED_ORIGINS = [\n+  \"https://fossbilling.org\",\n+  \"https://www.fossbilling.org\"\n+];\n+\n const statsV1 = new Hono<StatsEnv>();\n \n statsV1.use(\n   \"/*\",\n   cors({\n-    origin: \"*\"\n+    origin: (origin) => (origin ? ALLOWED_ORIGINS.includes(origin) : false)\n   }),\n   trimTrailingSlash()\n );\n"}]}]

💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Jan 14, 2026

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
api-worker bb3c9d2 Commit Preview URL

Branch Preview URL
Jan 14 2026, 12:18 PM

Copilot AI changed the title [WIP] Add integrity and crossorigin attributes to script tag for security Add CDN integrity, CORS restrictions, and absolute URL to stats API Jan 14, 2026
Copilot AI requested a review from admdly January 14, 2026 11:57
@admdly
Copy link
Contributor

admdly commented Jan 14, 2026

@copilot the CORS changes are not suitable as the endpoints are intended as public APIs. Think carefully and update the PR.

Copy link
Contributor Author

Copilot AI commented Jan 14, 2026

@copilot the CORS changes are not suitable as the endpoints are intended as public APIs. Think carefully and update the PR.

Reverted the CORS restrictions in commit bdf06d3. The endpoints now use origin: "*" to allow public access, while keeping the other security improvements (CDN integrity hash and absolute URL for fetch).

@admdly admdly marked this pull request as ready for review January 14, 2026 12:09
Copilot AI review requested due to automatic review settings January 14, 2026 12:09
Copilot AI and others added 4 commits January 14, 2026 12:09
- Add integrity and crossorigin attributes to Chart.js CDN script
- Fix regex pattern escaping in parsePhpVersion function
- Use absolute URL for stats data fetch request
- Restrict CORS to specific allowed origins
- Update integration tests for CORS changes

Co-authored-by: admdly <[email protected]>
The original double-escaped pattern /(\\d+\\.\\d+)/ is correct for template literals.
In a template literal, \\d becomes \d in the output, which is the proper regex escape.
The single-escaped version /(\d+\.\d+)/ would produce /(d+.d+)/ which is incorrect.

Co-authored-by: admdly <[email protected]>
Remove CORS allowlist and restore origin: "*" to allow public access.
The stats API endpoints are intended to be publicly accessible, so
the CORS restrictions were inappropriate.

Co-authored-by: admdly <[email protected]>
Copilot AI changed the title Add CDN integrity, CORS restrictions, and absolute URL to stats API Add CDN integrity and absolute URL to stats API Jan 14, 2026
Copilot AI requested a review from admdly January 14, 2026 12:10
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds security improvements to the stats API by implementing CDN resource integrity verification and using absolute URLs for API requests.

Changes:

  • Added integrity hash and crossorigin attributes to Chart.js CDN script for Subresource Integrity (SRI) verification
  • Changed fetch API call to use absolute URL constructed from window.location.origin instead of relative path

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@admdly admdly merged commit ae88260 into main Jan 14, 2026
4 checks passed
@admdly admdly deleted the copilot/add-integrity-attributes branch January 14, 2026 12:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants